;> Kernel
RELEASE * 0
MOUSETYPE * 1 ;0 for acorn hardware board, 1 for master mouse
 [ RELEASE=1
A5002P * 0 ;=1 for MEMC Executive
 |
A5002P * 1 ;=1 for a5002p
 ]
ADEV * 1
 [ A5002P=0
MEMC * 0
IOC * 0
VIDC * 0
M4SIZE * 0
 |
 [ RELEASE=0
IOC * 1 ;ioc/noioc
VIDC * 1
MEMC * 1
M4SIZE * 0 ;4m/1m
 |
IOC * 0
VIDC * 0
MEMC * 1
M4SIZE * 1
 ]
 ]
PTRBUG * 0
 ^ 0
WRITEC # 1
WRITES # 1
WRITE0 # 1
NEWLINE # 1
READC # 1
CLI # 1
BYTE # 1
WORD # 1
FILE # 1
ARGS # 1
BGET # 1
BPUT # 1
MULTIPLE # 1
OPEN # 1
READLINE # 1
CONTROL # 1
GETENV # 1
EXIT # 1
SETENV # 1
INTON # 1
INTOFF # 1
CALLBACK # 1
ENTERSWI # 1
BREAKPT # 1
BREAKCT # 1
UNUSEDSWI # 1
SETMEMC # 1
SETCALLBACK # 1
MOUSE # 1 ;<-
LASTSWI # 0
WRITEI * 256
ESCCNT * -4*26; from buffer
ESCFLG * ESCCNT+1
STRACC * &F00
BUFFER * &E00
DUMPER * &E40
BRKLST * &E80
IRQSTK * STRACC ;goes down!
SVCSTK * BUFFER
;Level0_currentThread    #       4  ; FE0
;Level0_freedQueue       #       4  ; FE4
;Level0_centisecondTime  #       4  ; FE8
;Level0_GFT              #       4  ; FEC
;Level0_ForceCrash       #       4  ; FF0
;Level0_FloatEmulatorWS  #       4  ; FF4
;Level0_Inhibit_Scheduler #      4  ; FF8
;MICK has FFC
 ORG 0
 [ A5002P=0
TUBE     * &1000000
CAM      * &1800000
IOCADR   * &1800000
VIDCADR  * &1800000
PHYSICAL * &2000000
ROM      * &3000000
OSMD     * &3800000
 |
PHYSICAL * &2000000
 [ ADEV=1
TUBE     * &3340000
 |
TUBE     * &3000000
 ]
IOCADR   * &3200000
VIDCADR  * &3400000
MEMCADR  * &3600000
ROM      * &3800000
CAM      * &3800000
OSMD     * &11111111
 ]
 ^ 0
R1STAT # 4
R1DATA # 4
R2STAT # 4
R2DATA # 4
R3STAT # 4
R3DATA # 4
R4STAT # 4
R4DATA # 4
 ^ 0
RDCHNO # 2
CLINO # 2
SBYTNO # 2
BYTENO # 2
WORDNO # 2
RDLNNO # 2
ARGSNO # 2
BGETNO # 2
BPUTNO # 2
FINDNO # 2
FILENO # 2
GBPBNO # 2

CFLAG * &20000000
IFLAG * &08000000
CCMASK * &FC000003

; The structure of the file control block table
; There are 16 FCBs for the 16 buffers
; In addition there are 256 bytes below that indicating file valid
; for each of the 256 handles in the rabge 256 - 511
 ^ 0
; The current pointer in the file
FCBPTR # 4
; The address of the buffer
FCBBUF # 4
; The extent of the file
FCBEXT # 4
; A bag of useful bits
; Meanings (for output files) are:-
; &80000000 set <=> this is an output file
;         1 set <=> there is a file open on this buffer
;         2 set <=> a read has been attempted already at EOF
;         4 set <=> buffer has been read off disc
FCBFLG # 4
OutputFileBit * &80000000
ReadFromDiscBit * 4
ReadAtEOFBit * 2
FileOpenOnBufferBit * 1
CR * 13
LF * 10

R0 RN 0
R1 RN 1
R2 RN 2
R3 RN 3
R4 RN 4
R5 RN 5
R6 RN 6
R7 RN 7
R8 RN 8
R9 RN 9
R10 RN 10
R11 RN 11
R12 RN 12
R13 RN 13
R14 RN 14
R15 RN 15
PC RN R15
SP RN R13
SPSVC RN R13
SPIRQ RN R13
TUBER RN R12
SVCWK0 RN R11
SVCWK1 RN R10
FIQADR RN R11
 B ROM+CONT
 LDR PC,UNDHAN
 B SVC
 LDR PC,PABHAN
 LDR PC,DABHAN
 LDR PC,ADXHAN
 B IRQ
FIQ MOV TUBER,#TUBE
 MOV TUBER,#TUBE
 MOV TUBER,#TUBE
 STRB TUBER,[TUBER,#R3DATA]
 SUBS PC,R14,#4
SVC STMFD SPSVC!,{TUBER,SVCWK0,SVCWK1}
 BIC TUBER,R14,#CCMASK
 LDR SVCWK0,[TUBER,#-4]
 BIC SVCWK0,SVCWK0,#&FF000000
 CMP SVCWK0,#512
 LDRCS PC,HISERV
 CMP SVCWK0,#ENTERSWI
 MOV SVCWK1,PC
 TEQCCP SVCWK1,#IFLAG ;re-enable interrupts for calls < ENTERSWI
 CMP SVCWK0,#WRITEI
 BCS SWRTI+ROM
 CMP SVCWK0,#LASTSWI
 BCS SLVK
 MOV TUBER,#TUBE
 ADR SVCWK1,JTABLE
 LDR PC,[SVCWK1,SVCWK0,LSL #2]
JTABLE & SWRTC
 & SWRTS+ROM
 & SWRT0
 & SNEWL
 & SRDC
 & SCLI
 & SBYTE
 & SWORD
 & SFILE
 & SARGS
 & SBGET
 & SBPUT
 & SMULT
 & SOPEN
 & SRDLN+ROM
 & SCTRL+ROM
 & SENV
 & SEXIT
 & SSTENV+ROM
 & SINTON+ROM
 & SINTOFF+ROM
 & SCALLB
 & SENTERSWI+ROM
 & SBRKPT+ROM
 & SBRKCT
 & SUNUSED+ROM
 & SSETMEMC+ROM
 & SSETCALL+ROM
 & SMOUSE+ROM ;<-
HISERV & SLVK
SWRTC LDRB SVCWK0,[TUBER,#R1STAT]
 TST SVCWK0,#&40
 BEQ SWRTC
 STRB R0,[TUBER,#R1DATA]
SLVK TEQP PC,#IFLAG+3 ;disable IRQs
 LDR SVCWK0,DOCALL
 TEQ SVCWK0,#1
 LDMNEFD SPSVC!,{TUBER,SVCWK0,SVCWK1}
 BICNES PC,R14,#&10000000 ;clear V flag
 MOV TUBER,#0 ;process call back
 STR TUBER,DOCALL
 LDR TUBER,CALLBF
 STMIA TUBER!,{R0,R1,R2,R3,R4,R5,R6,R7,R8,R9}
 LDMFD SPSVC!,{R0,R1,R2} ;get TUBER,SVCWK0,SVCWK1 (10,11,12)
 STMIA TUBER!,{R0,R1,R2,R13,R14}^ ;write those 3 and user's 13,14
 BIC R14,R14,#&10000000 ;clear V flag
 STMIA TUBER!,{R14} ;user's PC
 LDR PC,CALLAD
DOCALL & 0
SWRT0 STMFD SPSVC!,{R14}
 MOV SVCWK1,R0
SWRT01 LDRB R0,[SVCWK1],#1
 TEQ R0,#0
 SWINE WRITEC
 BNE SWRT01
 MOV R0,SVCWK1
 LDMFD SPSVC!,{R14}
 B SLVK
SNEWL STMFD SP!,{R0,R14}
 MOV R0,#10
 SWI WRITEC
 MOV R0,#13
 SWI WRITEC
 B SLVK1
SRDC STMFD SPSVC!,{R14}
 MOV R0,#RDCHNO
SRDC1 BL TUBEW2
SRDC2 BL TUBER2
 CMP R0,#&80 ;r2 bt 7 to C
 BL TUBER2
SRDC3 LDMFD SPSVC!,{R14}
 BIC R14,R14,#CFLAG
 ORRCS R14,R14,#CFLAG
 B SLVK
SBYTE STMFD SPSVC!,{R0,R14}
 MOV SVCWK1,R0
 CMP SVCWK1,#&7F
 BEQ SEOF
 BHI SBYTE1
SSBYTE MOV R0,#SBYTNO
 BL TUBEW2
 MOV R0,R1
 BL TUBEW2
 MOV R0,SVCWK1
 BL TUBEW2
 BL TUBER2
 MOV R1,R0
SLVK1 LDMFD SPSVC!,{R0,R14}
 B SLVK
SEOF CMP R1,#256
 BCC SSBYTE
 LDR SVCWK1,RAMLIMIT
 SUB SVCWK1,SVCWK1,#4*4*16+2*256
 LDRB SVCWK0,[SVCWK1,R1]
 TEQ SVCWK0,#0
 BEQ ChanErr+ROM
 ADD SVCWK1,SVCWK1,#2*256
 AND SVCWK0,R1,#&F
 ADD SVCWK1,SVCWK1,SVCWK0,LSL #4 ;address
 LDR SVCWK0,[SVCWK1,#FCBPTR]
 LDR R0,[SVCWK1,#FCBEXT]
 CMP SVCWK0,R0
 MOVCC R1,#0
 MOVCS R1,#-1
 B SLVK1
SBYTE1 MOV R0,#BYTENO
 BL TUBEW2
 MOV R0,R1
 BL TUBEW2
 MOV R0,R2
 BL TUBEW2
 MOV R0,SVCWK1
 BL TUBEW2
 CMP SVCWK1,#&9D
 BEQ SLVK1
 CMP SVCWK1,#&8E
 BL TUBER2
 STMFD SPSVC!,{R0}
 BL TUBER2
 MOV R2,R0
 BL TUBER2
 MOV R1,R0
 LDMFD SPSVC!,{SVCWK0}
 LDMFD SPSVC!,{R0,R14}
 BIC R14,R14,#CFLAG
 TST SVCWK0,#&80
 ORRNE R14,R14,#CFLAG
 B SLVK
SWORD TEQ R0,#0
 BEQ SLVK
 STMFD SPSVC!,{R0,R14}
 MOV SVCWK1,R0
 MOV R0,#WORDNO
 BL TUBEW2
 MOV R0,SVCWK1
 BL TUBEW2
 LDRB R0,[R1]
 CMP SVCWK1,#128
 BCS SWORD1
 MOV R0,#16
 CMP SVCWK1,#&15
 BCS SWORD1
 ADR SVCWK0,WORSEN
 LDRB R0,[SVCWK0,SVCWK1]
SWORD1 BL TUBEW2
 SUBS SVCWK0,R0,#1
 BMI SWORD4
SWORD2 LDRB R0,[R1,SVCWK0]
SWORD3 LDRB R14,[TUBER,#R2STAT]
 TST R14,#&40
 BEQ SWORD3
 STRB R0,[TUBER,#R2DATA]
 SUBS SVCWK0,SVCWK0,#1
 BPL SWORD2
SWORD4 LDRB R0,[R1,#1]
 CMP SVCWK1,#128
 BCS SWORD5
 MOV R0,#16
 CMP SVCWK1,#&15
 BCS SWORD5
 ADR SVCWK0,WORRCV
 LDRB R0,[SVCWK0,SVCWK1]
SWORD5 BL TUBEW2
 SUBS SVCWK1,R0,#1
 BMI SLVK1
SWORD6 BL TUBER2
 STRB R0,[R1,SVCWK1]
 SUBS SVCWK1,SVCWK1,#1
 BPL SWORD6
 B SLVK1
WORSEN = 0,0,5,0,5,4,5,8,&0E,4,1,1,5,0 ;&0D
 = 1,&20,&10 ;&10
 = &0D,0,4,&80 ;&14
WORRCV = 0,5,0,5,0,5,0,0,0,5,9,5,0,8 ;&0D
 = &18,0,1 ;&10
 = &0D,&80,4,&80 ;&14
 ALIGN
SFILE STMFD SPSVC!,{R0,R14}
 STMFD SPSVC!,{R2,R3,R4,R5} ;parms
 MOV R0,#FILENO
 BL TUBEW2
 MOV R2,#15
SFILE1 LDRB R0,[SP,R2]
 BL TUBEW2
 SUBS R2,R2,#1
 BPL SFILE1
 MOV SVCWK1,R1
 BL TUBEWS
 LDR R0,[SPSVC,#4*4]
 BL TUBEW2
 MOV R2,#16 ;read r1 parm too
SFILE2 BL TUBER2
 STRB R0,[SP,R2]
 SUBS R2,R2,#1
 BPL SFILE2
 LDMFD SPSVC!,{R2,R3,R4,R5}
 B SLVK1
SARGS STMFD SPSVC!,{R0,R14}
 CMP R1,#256
 BCC SARGSA ;Branch if not a buffered handle
 LDR SVCWK1,RAMLIMIT
 SUB SVCWK1,SVCWK1,#4*4*16+2*256
 LDRB SVCWK0,[SVCWK1,R1]
 TEQ SVCWK0,#0
 BEQ ChanErr+ROM
 AND R14,R1,#&F
 ADD SVCWK1,SVCWK1,#2*256
 ADD SVCWK1,SVCWK1,R14,LSL #4 ;control area address
 CMP R0,#1
 LDRCC R2,[SVCWK1,#FCBPTR] ;Read sequential pointer (0)
 BCC SLVK1
 BEQ SARGB1 ;Write sequential pointer (1)
 TEQ R0,#2
 LDREQ R2,[SVCWK1,#FCBEXT] ;Read extent
 B SLVK1
SARGB1
; First test if it's a move outside file operation
 MOV R14,#1024
 SUB R14,R14,#1
 LDR SVCWK0,[SVCWK1,#FCBEXT]
 CMP SVCWK0,R2
 LDRCS R0,[SVCWK1,#FCBPTR]
 BIC R0,R0,R14 ;Get block number of current pointer
 BIC R14,R2,R14 ;And block number of new pointer
 TEQCS R14,R0
 BLNE BUFFLUSH+ROM ;Flush if changing buffer or extending
; Ensure file system up to date if file being extended
 CMP SVCWK0,R2 ;See if extending
 STMFD SPSVC!,{R2} ;Remember new PTR
 MOV R0,#1 ;Write pointer
 BLCC SARGSUB+ROM ;Tell filing system if so
 LDMFD SPSVC!,{R2}
 STRCC R2,[SVCWK1,#FCBEXT] ;And update extent if we successfully moved
 STR R2,[SVCWK1,#FCBPTR]
 B SLVK1
; The non-buffered version of OSARGS
SARGSA
 BL SARGSUB+ROM
 STR R0,[SPSVC]
 B SLVK1
SBGET STMFD SPSVC!,{R14}
 CMP R1,#256
 BCS SBGETB
 MOV R0,#BGETNO
 BL TUBEW2
 MOV R0,R1
 B SRDC1
SBGETB LDR SVCWK1,RAMLIMIT
 SUB SVCWK1,SVCWK1,#4*4*16+2*256
 LDRB SVCWK0,[SVCWK1,R1]
 TEQ SVCWK0,#0
 BEQ ChanErr+ROM
 ADD SVCWK1,SVCWK1,#2*256
 AND R0,R1,#&F
 ADD SVCWK1,SVCWK1,R0,LSL #4 ;control area address
 LDR R14,[SVCWK1,#FCBFLG]
 TST R14,#ReadAtEOFBit
 BNE EOFError+ROM ;Second read at end of file
 LDR SVCWK0,[SVCWK1,#FCBPTR]
 LDR R0,[SVCWK1,#FCBEXT]
 CMP SVCWK0,R0
 BHI EOFError+ROM ;read past end of file
 BEQ SBGTB2 ;set flag: at end of file
; Note C = 0 here, as required by successful exit from BGET
 LDR R0,[SVCWK1,#FCBFLG]
 TST R0,#ReadFromDiscBit
 BLEQ BUFGET+ROM ;Read buffer from disc if necessary
SBGTBX MOV R0,#1024
 SUB R0,R0,#1
 AND R14,SVCWK0,R0
 TEQ R14,R0
 BLEQ BUFFLUSH+ROM ;Flush out old buffer if it was an output buffer
 ADD R14,SVCWK0,#1
 STR R14,[SVCWK1,#FCBPTR] ;Increment pointer
 AND SVCWK0,SVCWK0,R0
 LDR R0,[SVCWK1,#FCBBUF]
 LDRB R0,[R0,SVCWK0] ;Get the byte from the file
 B SRDC3
SBGTB2 LDR R0,[SVCWK1,#FCBFLG]
 ORR R0,R0,#ReadAtEOFBit ;Mark one read done at EOF
 STR R0,[SVCWK1,#FCBFLG]
 MOV R0,#&FE
 B SRDC3 ;carry set
SBPUT STMFD SPSVC!,{R0,R14}
 CMP R1,#256
 BCS SBPUTB
 MOV R0,#BPUTNO
 BL TUBEW2
 MOV R0,R1
 BL TUBEW2
 LDR R0,[SPSVC]
 BL TUBEW2
 BL TUBER2
 B SLVK1
SBPUTB LDR SVCWK1,RAMLIMIT
 SUB SVCWK1,SVCWK1,#4*4*16+2*256
 LDRB SVCWK0,[SVCWK1,R1]
 TEQ SVCWK0,#0
 BEQ ChanErr+ROM
 ADD SVCWK1,SVCWK1,#2*256
 AND SVCWK0,R1,#&F
 ADD SVCWK1,SVCWK1,SVCWK0,LSL #4 ;control area address
 LDR R14,[SVCWK1,#FCBFLG]
 TST R14,#ReadFromDiscBit
 BLEQ BUFGET+ROM ;Get buffer in store if necessary
 LDR SVCWK0,[SVCWK1,#FCBPTR]
 MOV R14,#1024
 SUB R14,R14,#1
 AND R14,SVCWK0,R14
 LDR SVCWK0,[SVCWK1,#FCBBUF]
 STRB R0,[SVCWK0,R14]
 LDR SVCWK0,[SVCWK1,#FCBPTR]
 ADD SVCWK0,SVCWK0,#1 ;Up pointer
 LDR R0,[SVCWK1,#FCBEXT]
 CMP SVCWK0,R0
 STRCS SVCWK0,[SVCWK1,#FCBEXT] ;Update extent if necessary
 LDR R0,[SVCWK1,#FCBFLG]
 BIC R0,R0,#ReadAtEOFBit ;No longer reading at EOF, whatever.
 STR R0,[SVCWK1,#FCBFLG]
 ADD R14,R14,#1
 TST R14,#1024 ;See if got buffer full
 BLNE BUFFLUSH+ROM
 STR SVCWK0,[SVCWK1,#FCBPTR] ;Update pointer anyway
 B SLVK1
SMULT STMFD SPSVC!,{R14}
 TEQ R0,#0
 BEQ SMULT0 ;Branch if not a file transfer call
 CMP R0,#4+1
 BCS SMULT0 ;Branch if not a file transfer call
 CMP R1,#256
 BCC SMULT0 ;Branch if not a buffered handle
 LDR R14,RAMLIMIT
 SUB R14,R14,#4*4*16+2*256
 LDRB SVCWK0,[R14,R1] ;See if file open on this handle number
 TEQ SVCWK0,#0
 BEQ ChanErr+ROM ;Moan if not
; Now for the difficult bit!
; First see if call requires reset of PTR first
 STMFD SPSVC!,{R0,R1,R2,R3,R4}
 TST R0,#1
 BEQ SMULT10 ;Branch if read/write at PTR
 MOV R0,#1
 MOV R2,R4
 SWI ARGS
 LDMFD SPSVC,{R0,R1,R2} ;And get corrupted registers back
SMULT10
 TEQ R3,#0,2 ;Check we've got something to do after all this!
; Note carry forced to clear by above operation!
; I'd like to see a compiler generate that one!
 LDR R14,RAMLIMIT
 SUB SVCWK1,R14,#4*4*16
 AND SVCWK0,R1,#&F
 ADD SVCWK1,SVCWK1,SVCWK0,LSL #4
 LDMEQFD SPSVC!,{R0,R1,R2,R3,R4}
 LDREQ R4,[SVCWK1,#FCBPTR] ;Pointer may have changed even so!
 BEQ SRDC3 ;update carry
 LDR R14,[SVCWK1,#FCBFLG]
 TST R14,#ReadFromDiscBit
 BLEQ BUFGET+ROM ;Ensure buffer contents valid
 SUB R0,R0,#1 ;Put operation number in the range 0 - 3
 MOVS R0,R0,LSR #1
; R4 is now a spare register
; R0 = 0 => Put byte
; R0 = 1 => Get byte
 LDR SVCWK0,[SVCWK1,#FCBBUF] ;Pointer to start of buffer
 LDR R4,[SVCWK1,#FCBPTR] ;Offset in it
 MOV R14,#1024
 SUB R14,R14,#1
 AND R14,R4,R14 ;Offset in buffer
 LDR R0,[SVCWK1,#FCBEXT]
 BEQ SMULT50 ;Branch if put bytes
; Get bytes
SMULT20
 CMP R0,R4 ;Are we at end of file?
 BEQ SMULT40 ;Branch if so
 CMP R14,#1024 ;Are we at the end of the buffer?
 BEQ SMULT40 ;Branch if so
 LDRB R1,[SVCWK0,R14] ;Get the byte at last!
 ADD R14,R14,#1 ;Up count in buffer
 STRB R1,[R2],#1 ;And pass it to the user
 ADD R4,R4,#1 ;Update pointer
 SUBS R3,R3,#1
 BNE SMULT20
 CMP R3,#1 ;Clear carry
; Here we have successfully completed the user's request
SMULT30
 STR R4,[SVCWK1,#FCBPTR] ;Update my copy of the pointer
 ADD SPSVC,SPSVC,#5*4 ;Ignore stacked information
SMULTX B SRDC3 ;And exit giving carry change
SMULT40
; Here we are at the end of the buffer or the file, with some still to read
; Do the remaining read as an ordinary GBPB, but mark new buffer unread
 MOV R0,#3 ;Read using my pointer
 B SMULT70
SMULT50
; Put bytes
 CMP R14,#1024 ;Are we at the end of the buffer?
 BEQ SMULT60 ;Branch if so
 LDRB R1,[R2],#1 ;Get the byte from the user
 STRB R1,[SVCWK0,R14] ;And put it in the buffer
 ADD R14,R14,#1 ;Up count in buffer
 ADD R4,R4,#1 ;Update pointer
 SUBS R3,R3,#1
 BNE SMULT50 ;Loop if more to do
; Here we have completed the user's request within the buffer
 CMP R4,R0
 STRHI R4,[SVCWK1,#FCBEXT] ;Update EXT if written off end of existing file
 CMP R14,#1024 ;Clear carry
 B SMULT30
SMULT60
; Here we are at the end of the buffer, with some still to put
; Do the remaining put as an ordinary GBPB, but mark new buffer unread
 CMP R4,R0
 STRHI R4,[SVCWK1,#FCBEXT] ;Update extent if writen off end of file already
 MOV R0,#1 ;Put using my pointer
SMULT70
 LDR R1,[SPSVC,#4] ;Get R1 back off stack
 BL BUFFLUSH+ROM ;Flush out current buffer if necessary
 AND R1,R1,#&FF ;Use unbuffered version
 SWI MULTIPLE
; Now see if EXT needs updating
 MOV R1,#0
 ADC R1,R1,#0;Add in the carry
 LDR R0,[SVCWK1,#FCBEXT]
 CMP R4,R0
 STRCS R4,[SVCWK1,#FCBEXT] ;Update EXT if extended file
 CMP R1,#1 ;Reset carry
 B SMULT30 ;And exit
SMULT0 STMFD SPSVC!,{R0}
 STMFD SPSVC!,{R2,R3,R4}
 MOV R0,#GBPBNO
 BL TUBEW2
 MOV SVCWK1,#11
SMULT1 LDRB R0,[SPSVC,SVCWK1]
 BL TUBEW2
 SUBS SVCWK1,SVCWK1,#1
 BPL SMULT1
 MOV R0,R1
 BL TUBEW2
 LDR R0,[SPSVC,#3*4]
 BL TUBEW2
 MOV SVCWK1,#11
SMULT2 BL TUBER2
 STRB R0,[SPSVC,SVCWK1]
 SUBS SVCWK1,SVCWK1,#1
 BPL SMULT2
 BL TUBER2
 LDMFD SPSVC!,{R2,R3,R4}
 LDMFD SPSVC!,{R0}
 B SRDC2
SOPEN STMFD SPSVC!,{R0,R14}
 TEQ R0,#0
 BNE SOPEN0
 CMP R1,#0 ;See if close#0
 BLEQ CloseHash0+ROM
SOPEN10
 CMP R1,#256
 BCC SOPEN0
 LDR SVCWK1,RAMLIMIT
 SUB SVCWK1,SVCWK1,#4*4*16+2*256
 LDRB R0,[SVCWK1,R1] ;See if file open here at all
 TEQ R0,#0
 BEQ ChanErr+ROM ;Branch if not
 MOV R0,#0
 STRB R0,[SVCWK1,R1] ;Closing it now
 ADD SVCWK1,SVCWK1,#2*256
 AND R0,R1,#&F
 ADD SVCWK1,SVCWK1,R0,LSL #4 ;control area address
 BL BUFFLUSH+ROM ;Flush if output file
 MOV R0,#0
 STR R0,[SVCWK1,#FCBFLG]
SOPEN0 MOV SVCWK1,R0
 MOV R0,#FINDNO
 BL TUBEW2
 MOV R0,SVCWK1
 BL TUBEW2
 TEQ R0,#0
 BNE SOPEN1
 MOV R0,R1 ;a close
 BL TUBEW2
 BL TUBER2
 B SLVK1
SOPEN1 STMFD SPSVC!,{SVCWK1}
 MOV SVCWK1,R1
 BL TUBEWS
 BL TUBER2
 LDMFD SPSVC!,{SVCWK1}
 TEQ R0,#0
 BEQ SOPEN2 ;Branch if couldn't open
 LDRB SVCWK0,BUFFLAG
 TEQ SVCWK0,#0
 BEQ SOPEN2 ;Branch if buffering disabled
; Now see if buffer for this handle available
 LDR SVCWK0,RAMLIMIT
 STMFD SPSVC!,{R1}
 AND R1,R0,#&F
 ADD SVCWK0,SVCWK0,R1,LSL #4 ;Access relevant FCB
 LDR SVCWK0,[SVCWK0,#FCBFLG-4*4*16] ;FCBs live below RAMLIMIT
 LDMFD SPSVC!,{R1}
 TST SVCWK0,#FileOpenOnBufferBit ;Is another handle already using this buffer?
 BNE SOPEN2 ;Branch if so
 TEQ SVCWK1,#&40
 BEQ SOPNBI
 TEQ SVCWK1,#&80
; TEQNE SVCWK1,#&C0 ;Branch if some sort of output file
 BEQ SOPNBO
SOPEN2 STR R0,[SPSVC] ;put r0 onto stack over original version
 B SLVK1
SOPNBI
 ORR R0,R0,#256
 STR R0,[SPSVC]
; Recurse SVC to do OSARGS 2
 STMFD SPSVC!,{R1,R2} ;Preserve user's registers
 AND R1,R0,#&FF ;Handle
 MOV R0,#2 ;Read extent
 SWI ARGS
 LDR SVCWK0,RAMLIMIT
 SUB SVCWK0,SVCWK0,#4*4*16+256
 MOV SVCWK1,#FileOpenOnBufferBit
 STRB SVCWK1,[SVCWK0,R1] ;Mark this file handle valid
 ADD SVCWK0,SVCWK0,#256
 AND SVCWK1,R1,#&F ;FCB number
 MOV R0,SVCWK0
 ADD SVCWK0,SVCWK0,SVCWK1,LSL #4 ;Access relevant FCB
 STR R2,[SVCWK0,#FCBEXT] ;Save the extent
 MOV R1,#0
 STR R1,[SVCWK0,#FCBPTR] ;Save the pointer
 SUB R0,R0,#256+16*1024 ;Start of buffer
 ADD R0,R0,SVCWK1,LSL #10 ;1024 bytes per buffer
 STR R0,[SVCWK0,#FCBBUF] ;Save the start of buffer
 MOV R0,#FileOpenOnBufferBit
 STR R0,[SVCWK0,#FCBFLG] ;Note that no buffer read yet
 LDMFD SPSVC!,{R1,R2}
 B SLVK1
SOPNBO
 ORR R0,R0,#256
 STR R0,[SPSVC]
 AND R0,R0,#&FF
 LDR R14,RAMLIMIT
 SUB SVCWK0,R14,#4*4*16+256
 MOV SVCWK1,#FileOpenOnBufferBit
 STRB SVCWK1,[SVCWK0,R0] ;Mark this file handle valid
 ADD SVCWK0,SVCWK0,#256
 AND R0,R0,#&F ;Buffer number
 ADD SVCWK0,SVCWK0,R0,LSL #4 ;control area address
 SUB R14,R14,#4*4*16+256+16*1024
 ADD R14,R14,R0,LSL #10
 STR R14,[SVCWK0,#FCBBUF] ;Address of buffer
 MOV R0,#0
 STR R0,[SVCWK0,#FCBPTR] ;Pointer = 0
; Recurse SVC to do OSARGS 2
 STMFD SPSVC!,{R1,R2} ;Preserve user's registers
 LDR R1,[SPSVC,#8] ;Handle
 AND R1,R1,#&FF ;Read it from the Beeb!
 MOV R0,#2 ;Read extent
 SWI ARGS
 STR R2,[SVCWK0,#FCBEXT]
 LDMFD SPSVC!,{R1,R2}
SOPNBO10
 MOV R0,#OutputFileBit+FileOpenOnBufferBit
 STR R0,[SVCWK0,#FCBFLG]
 B SLVK1
SENV MOV R0,#BUFFER ;environment string
 LDR R1,MEMLIMIT
 ADR R2,START
 B SLVK
SEXIT LDMFD SPSVC!,{TUBER,SVCWK0,SVCWK1}
 LDR PC,SEXITA
SCALLB LDR SVCWK0,CALLBF
 TEQ R0,#0
 STRNE R0,CALLBF
 MOV R0,SVCWK0
 LDR SVCWK0,CALLAD
 TEQ R1,#0
 STRNE R1,CALLAD
 MOV R1,SVCWK0
 B SLVK
MEMCIMAGE
 [ M4SIZE=1
 & &E030C
 |
 & &E0304
 ]
MEMLIMIT & &4000 ;"logical" amount
RAMLIMIT & &4000 ;"real" amount
SEXITA & CLIEXIT+ROM
ERRHAN & ERRORH+ROM
ERRBUF & BUFFER
ESCHAN & ESCAPH+ROM
EVTHAN & EVENTH+ROM
UNDHAN & UNDEF+ROM
PABHAN & ABORTP+ROM
DABHAN & ABORTD+ROM
ADXHAN & ADDREX+ROM
CALLAD & NOCALL+ROM
START & -1
 = &FF
BUFFLAG = 0,0,0
CALLBF & BUFFER
BRKAD & DEFBRK+ROM
BRKBF & DUMPER
CACHENAMES & 0
SBRKCT LDR SVCWK0,BRKBF
 TEQ R0,#0
 STRNE R0,BRKBF
 MOV R0,SVCWK0
 LDR SVCWK0,BRKAD
 TEQ R1,#0
 STRNE R1,BRKAD
 MOV R1,SVCWK0
 B SLVK
SCLI STMFD SPSVC!,{R0,R14}
 MOV SVCWK1,R0
 MOV SVCWK0,R0
SCLI1 LDRB R0,[SVCWK0],#1
 TEQ R0,#" "
 TEQNE R0,#"*"
 BEQ SCLI1 ;Ignore spaces and stars
 CMP R0,#"a"
 BICCS R0,R0,#" "
 STMFD SPSVC!,{R1,R2,R3,R4}
 ADR R14,CACHENAMES
SCLINEXTCACHE LDR R14,[R14]
;points to
;    link
;    name (bytes terminated by 0)
; --become word aligned
;    length
;    exec
;    load
;    -checksum
;    <data>
 TEQ R14,#0
 BEQ SCLI1A
 ADD R1,R14,#4
 LDRB R2,[R1],#1
 CMP R0,R2
 BNE SCLINEXTCACHE
 MOV R3,SVCWK0
SCLICACHE1 LDRB R2,[R1],#1
 LDRB R4,[R3],#1
 CMP R4,#"a"
 BICCS R4,R4,#" "
 CMP R2,R4
 BEQ SCLICACHE1
 CMP R2,#0
 BNE SCLINEXTCACHE
 CMP R4,#" "
 BHI SCLINEXTCACHE
 ADD R1,R1,#3
 BIC R1,R1,#3
 LDMIA R1!,{R0,R7,R8,R9} ;length,exec,load,-checksum
SCLICACHEGOT LDR R2,[R1],#4
 ADD R9,R9,R2
 STR R2,[R8],#4
 SUBS R0,R0,#4
 BHI SCLICACHEGOT
 B SCLICACHEGO+ROM
SCLI1A LDMFD SPSVC!,{R1,R2,R3,R4}
 TEQ R0,#"H"
 BEQ SCLIGOTH+ROM
 TEQ R0,#"G"
 BEQ SCLIGOTG+ROM
 TEQ R0,#"C"
 BEQ SCLIGOTC+ROM
SCLIT STMFD SPSVC!,{SVCWK1} ;save ptr to date
 MOV SVCWK0,#BUFFER ;environment string
SCLIT1 LDRB R0,[SVCWK1],#1
 TEQ R0,#" "
 TEQNE R0,#"*"
 TEQNE R0,#"/"
 BEQ SCLIT1
SCLIT2 STRB R0,[SVCWK0],#1
 LDRB R0,[SVCWK1],#1
 TEQ R0,#0
 TEQNE R0,#10
 TEQNE R0,#13
 BNE SCLIT2
 MOV R0,#0
 STRB R0,[SVCWK0],#1
 LDMFD SPSVC!,{SVCWK1} ;load ptr again
 MOV R0,#CLINO
 BL TUBEW2
 BL TUBEWS
 BL TUBER2
 CMP R0,#&80
 BNE SLVK1
 B SCLIEXECUTE+ROM
IRQ SUB R14,R14,#4
 STMFD SPIRQ!,{SVCWK0,TUBER,R14}
 MOV TUBER,#TUBE
 LDRB SVCWK0,[TUBER,#R4STAT]
 CMP SVCWK0,#&80
 BCS IRQ4
 LDRB SVCWK0,[TUBER,#R1STAT]
 CMP SVCWK0,#&80
 BCC NOIRQ+ROM
 LDRB SVCWK0,[TUBER,#R1DATA]
 CMP SVCWK0,#&80
 BCC IRQEVT
 MOV TUBER,#0
 TST R14,#3
 MVNNE TUBER,#0
 ADR R14,ESCEXIT
 LDR PC,ESCHAN
ESCEXIT STR R12,DOCALL
 LDMFD SPIRQ!,{SVCWK0,TUBER,PC}^
IRQERR STMFD SPIRQ!,{R0,SVCWK1}
 LDR SVCWK1,ERRBUF
 STR R14,[SVCWK1],#4 ;correct if unexpected error
 BL TUBER2
 BL TUBER2
 STR R0,[SVCWK1],#4
IRQER1 BL TUBER2
 STRB R0,[SVCWK1],#1
 CMP R0,#0
 BNE IRQER1
 LDMFD SPIRQ!,{R0,SVCWK1,SVCWK0,TUBER,R14} ;reset SPIRQ stack
 TEQP PC,#3
 BNV 0
 TEQ SPSVC,#SVCSTK
 BEQ IRQER2
 SUB SVCWK1,SPSVC,#SVCSTK
 MOV SPSVC,#SVCSTK
 LDR SVCWK0,ERRBUF
 CMN SVCWK1,#3*4
 LDRNE R14,[SPSVC,#-4*4]
 STR R14,[SVCWK0]
 LDMDB SPSVC,{SVCWK1,SVCWK0,TUBER}
IRQER2 TSTP PC,#0
 LDR PC,ERRHAN
IRQEVT STMFD SPIRQ!,{R0,R1,R2}
 BL TUBER1
 MOV R2,R0
 BL TUBER1
 MOV R1,R0
 BL TUBER1
 LDR R14,[SPIRQ,#5*4]
 MOV TUBER,#0
 TST R14,#3
 MVNNE TUBER,#0
 ADR R14,EVTEXIT
 LDR PC,EVTHAN
EVTEXIT STR R12,DOCALL
 LDMFD SPIRQ!,{R0,R1,R2,SVCWK0,TUBER,PC}^
TUBER1IRQ STMFD SPIRQ!,{R14}
 ADR R14,TUBER1
 ORR R14,R14,#&FC000002
 B IRQ
 LDMFD SPIRQ!,{R14} ;This line can be executed due to the incredibly obscure Roger code
TUBER1 LDRB SVCWK0,[TUBER,#R4STAT]
 CMP SVCWK0,#&80
 BCS TUBER1IRQ
 LDRB SVCWK0,[TUBER,#R1STAT]
 TST SVCWK0,#&80
 BEQ TUBER1
 LDRB R0,[TUBER,#R1DATA]
 MOV PC,R14
IRQ4 LDRB SVCWK0,[TUBER,#R4DATA]
 CMP SVCWK0,#&80
 BCS IRQERR
 STMFD SPIRQ!,{R0,SVCWK1} ;SVCWK0 is data transfer 0-7
 BL TUBER4 ;read id of claimer to svcwk1
 TEQ SVCWK0,#5
 BEQ IRQEND
 BL TUBER4
 MOV R0,SVCWK1,LSL #24
 BL TUBER4
 ORR R0,R0,SVCWK1,LSL #16
 BL TUBER4
 ORR R0,R0,SVCWK1,LSL #8
 BL TUBER4
 ORR R0,R0,SVCWK1
 TEQ SVCWK0,#1
 TEQNE SVCWK0,#3
 TEQNE SVCWK0,#7
 BNE IRQ5
 CMP R0,#STRACC
 ADDCC R0,R0,#ROM
IRQ5 TEQP PC,#1+IFLAG
 MOV TUBER,#TUBE
 MOV FIQADR,R0
 TEQP PC,#2+IFLAG
 ADR SVCWK1,FIQTAB
 LDR SVCWK1,[SVCWK1,SVCWK0,LSL #2] ;routine
 MOV R14,#FIQ
IRQA5 LDR R0,[SVCWK1],#4
 STR R0,[R14],#4
 TEQ R14,#SVC
 BNE IRQA5
 LDRB R0,[TUBER,#R3DATA]
 LDRB R0,[TUBER,#R3DATA]
 TEQP PC,#1+IFLAG
 MOV TUBER,#TUBE
 MOV R0,FIQADR
 TEQP PC,#2+IFLAG
 BL TUBER4
 MOV SVCWK1,#256
 CMP SVCWK0,#6
 BCC IRQEND
;immediate data transfer. R0 is base address
 BNE FIQP7
FIQP6 LDRB SVCWK0,[TUBER,#R3STAT]
 TST SVCWK0,#&80
 BEQ FIQP6
 LDRB SVCWK0,[R0],#1
 STRB SVCWK0,[TUBER,#R3DATA]
 SUBS SVCWK1,SVCWK1,#1
 BNE FIQP6
FIQP6A LDRB SVCWK0,[TUBER,#R3STAT]
 TST SVCWK0,#&80
 BEQ FIQP6A
 STRB SVCWK0,[TUBER,#R3DATA] ;refill reg
 B IRQEND
FIQP7 LDRB SVCWK0,[TUBER,#R3STAT]
 TST SVCWK0,#&80
 BEQ FIQP7
 LDRB SVCWK0,[TUBER,#R3DATA]
 STRB SVCWK0,[R0],#1
 SUBS SVCWK1,SVCWK1,#1
 BNE FIQP7
IRQEND LDMFD SPIRQ!,{R0,SVCWK1,SVCWK0,TUBER,PC}^
FIQTAB & FIQP0+ROM
 & FIQP1+ROM
 & FIQP2+ROM
 & FIQP3+ROM
 & FIQDEF+ROM
 & FIQDEF+ROM
 & FIQDEF+ROM
 & FIQDEF+ROM
TUBEDB LDRB SVCWK0,[TUBER,#R2STAT]
 TST SVCWK0,#&40
 BEQ TUBEDB
 MOV R0,R0,ROR #24  ;HNML > NMLH
 STRB R0,[TUBER,#R2DATA]
TUBED1 LDRB SVCWK0,[TUBER,#R2STAT]
 TST SVCWK0,#&40
 BEQ TUBED1
 MOV R0,R0,ROR #24 ;MLHN
 STRB R0,[TUBER,#R2DATA]
TUBED2 LDRB SVCWK0,[TUBER,#R2STAT]
 TST SVCWK0,#&40
 BEQ TUBED2
 MOV R0,R0,ROR #24 ;LHNM
 STRB R0,[TUBER,#R2DATA]
 MOV R0,R0,ROR #24 ;HNML
TUBEW2 LDRB SVCWK0,[TUBER,#R2STAT]
 TST SVCWK0,#&40
 BEQ TUBEW2
 STRB R0,[TUBER,#R2DATA]
 MOV PC,R14
TUBEWS LDRB SVCWK0,[TUBER,#R2STAT]
 TST SVCWK0,#&40
 BEQ TUBEWS
 LDRB R0,[SVCWK1],#1
 TEQ R0,#10
 TEQNE R0,#0
 TEQNE R0,#13
 MOVEQ R0,#13
 STRB R0,[TUBER,#R2DATA]
 BNE TUBEWS
 MOV PC,R14
TUBER2 LDRB SVCWK0,[TUBER,#R2STAT]
 TST SVCWK0,#&80
 BEQ TUBER2
 LDRB R0,[TUBER,#R2DATA]
 MOV PC,R14
TUBER4 LDRB SVCWK1,[TUBER,#R4STAT]
 TST SVCWK1,#&80
 BEQ TUBER4
 LDRB SVCWK1,[TUBER,#R4DATA]
 MOV PC,R14
 LNK Middle
